`include "defines.v"
module wbu(
  input clk,
  input rst_n,
  input ctrl_wbu_flush_i,
  /* exu to wbu signal */
  input                 exu_wbu_valid_i ,
  output                wbu_exu_ready_o ,
  input                 exu_wbu_wen_i   ,
  input  [4:0]          exu_wbu_rd_i    ,
  input  [`XLEN-1:0]    exu_wbu_data_i  , 
  input  [`ADDR_W-1:0]  exu_wbu_pc_i    ,
  input  [31:0]         exu_wbu_inst_i  ,
  input  [`EXCEPT:0]    exu_wbu_exception_i  ,
  input                 exu_wbu_wr_csr_en_i  ,
  input  [11:0]         exu_wbu_wr_csr_addr_i,
  input  [`XLEN-1:0]    exu_wbu_wr_csr_data_i,
  input                 exu_wbu_redict_valid_i,
  input [`VADDR_W-1:0]  exu_wbu_redict_pc_i   ,
  input [`BRU_MSG_W-1:0]exu_wbu_bru_message_i ,
  /* wbu bypass signal & rf write enable signal*/
  output               wbu_isu_wen_o   ,  
  output [4:0]         wbu_isu_addr_o  ,  
  output [`XLEN-1:0]   wbu_isu_data_o  ,
  /* wbu csr bypass to exu */ 
  output               wbu_exu_wr_csr_en_o  ,
  output [11:0]        wbu_exu_wr_csr_addr_o,
  output [`XLEN-1:0]   wbu_exu_wr_csr_data_o,
  /* wbu csr write */
  output               wbu_wr_csr_en_o    ,
  output [`EXCEPT:0]   wbu_csr_exception_o,
  output [11:0]        wbu_wr_csr_addr_o  ,
  output [`XLEN-1:0]   wbu_wr_csr_data_o  ,
  output [`VADDR_W-1:0]wbu_csr_pc_o       ,
  output [31:0]        wbu_csr_inst_o     ,
  /* 特殊端口 */
  output               wbu_csr_commit_o   ,
  input                csr_wbu_isClint_i,
`ifdef DIFFTEST
  input                debug_selClint,
  input [7:0] debug_a0,
`endif
  // CSR in 
  input                csr_wbu_commit_flush_i,
  input [`VADDR_W-1:0] csr_wbu_commit_pc_i   ,

  output                  wbu_ifu_redict_valid_o ,
  output [`VADDR_W-1:0]   wbu_ifu_redict_pc_o    ,
  output [`BRU_MSG_W-1:0] wbu_ifu_bru_message_o  
);
reg               wbuValid          ;
reg               q_exu_wbu_wen_i   ;
reg [4:0]         q_exu_wbu_rd_i    ;
reg [`XLEN-1:0]   q_exu_wbu_data_i  ;
reg [`ADDR_W-1:0] q_exu_wbu_pc_i    ;
reg [31:0]        q_exu_wbu_inst_i  ;
reg [`EXCEPT:0]   q_exu_wbu_exception_i  ;
reg               q_exu_wbu_wr_csr_en_i  ;
reg [11:0]        q_exu_wbu_wr_csr_addr_i;
reg [`XLEN-1:0]   q_exu_wbu_wr_csr_data_i;
reg                 q_exu_wbu_redict_valid_i;
reg [`VADDR_W-1:0]  q_exu_wbu_redict_pc_i   ;
reg [`BRU_MSG_W-1:0]q_exu_wbu_bru_message_i ;

wire exu_wbu_hs = exu_wbu_valid_i && wbu_exu_ready_o;
assign wbu_exu_ready_o = 1'b1;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    wbuValid <= 1'b0;
  else if(ctrl_wbu_flush_i)
    wbuValid <= 1'b0;
  else if(wbu_exu_ready_o)
    wbuValid <= exu_wbu_valid_i;

always@(posedge clk)
  if(exu_wbu_hs)begin
    q_exu_wbu_wen_i  <= exu_wbu_wen_i ;
    q_exu_wbu_rd_i   <= exu_wbu_rd_i  ;
    q_exu_wbu_data_i <= exu_wbu_data_i;
    q_exu_wbu_pc_i   <= exu_wbu_pc_i  ;
    q_exu_wbu_inst_i <= exu_wbu_inst_i;
    q_exu_wbu_exception_i   <= exu_wbu_exception_i  ;
    q_exu_wbu_wr_csr_en_i   <= exu_wbu_wr_csr_en_i  ;
    q_exu_wbu_wr_csr_addr_i <= exu_wbu_wr_csr_addr_i;
    q_exu_wbu_wr_csr_data_i <= exu_wbu_wr_csr_data_i;
    q_exu_wbu_redict_valid_i <= exu_wbu_redict_valid_i;
    q_exu_wbu_redict_pc_i    <= exu_wbu_redict_pc_i   ;
    q_exu_wbu_bru_message_i  <= exu_wbu_bru_message_i ;
  end
assign wbu_isu_wen_o   = q_exu_wbu_wen_i &&  wbuValid && ~csr_wbu_isClint_i;
assign wbu_isu_addr_o  = q_exu_wbu_rd_i  ;
assign wbu_isu_data_o  = q_exu_wbu_data_i;
/* wbu csr bypass to exu */ 
assign wbu_exu_wr_csr_en_o   = q_exu_wbu_wr_csr_en_i  && wbuValid ;
assign wbu_exu_wr_csr_addr_o = q_exu_wbu_wr_csr_addr_i;
assign wbu_exu_wr_csr_data_o = q_exu_wbu_wr_csr_data_i;
/* wbu csr write */
assign wbu_wr_csr_en_o      = wbu_exu_wr_csr_en_o ;
assign wbu_csr_exception_o  = {`EXCEPT{wbuValid}} & q_exu_wbu_exception_i;
assign wbu_wr_csr_addr_o    = wbu_exu_wr_csr_addr_o;
assign wbu_wr_csr_data_o    = wbu_exu_wr_csr_data_o;
assign wbu_csr_commit_o     = wbuValid;
assign wbu_csr_pc_o         = q_exu_wbu_pc_i  ;
assign wbu_csr_inst_o       = q_exu_wbu_inst_i;
/* redict  */
assign wbu_ifu_redict_valid_o = ((wbuValid && q_exu_wbu_redict_valid_i) || csr_wbu_commit_flush_i);
assign wbu_ifu_redict_pc_o    = csr_wbu_commit_flush_i ? csr_wbu_commit_pc_i  : q_exu_wbu_redict_pc_i;
assign wbu_ifu_bru_message_o  = q_exu_wbu_bru_message_i;

/* simulation test code */
`ifdef DIFFTEST
reg q_exu_wbu_valid;
reg[`ADDR_W-1:0] q_exu_wbu_pc   ; 
reg[31:0] q_exu_wbu_inst ;   
reg q_wbu_isu_wen  ; 
reg[4:0] q_wbu_isu_addr ;   
reg[`XLEN-1:0] q_wbu_isu_data ;   
reg q_exu_wbu_wr_csr_en;
reg [11:0] q_exu_wbu_wr_csr_addr;
reg debug_selClintR ,debug_selClintRR;
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    q_exu_wbu_valid <= 'd0;
    q_exu_wbu_pc    <= 'd0;
    q_exu_wbu_inst  <= 'd0;
    q_wbu_isu_wen   <= 'd0;
    q_wbu_isu_addr  <= 'd0;
    q_wbu_isu_data  <= 'd0;
    q_exu_wbu_wr_csr_en <= 'd0;
    q_exu_wbu_wr_csr_addr <= 'd0;
    debug_selClintR <= 'd0;
    debug_selClintRR <= 'd0;
  end else begin
    q_exu_wbu_valid <= wbuValid && ~csr_wbu_isClint_i;
    q_exu_wbu_pc    <= q_exu_wbu_pc_i   ;
    q_exu_wbu_inst  <= q_exu_wbu_inst_i ;
    q_wbu_isu_wen   <= wbu_isu_wen_o ;
    q_wbu_isu_addr  <= wbu_isu_addr_o;
    q_wbu_isu_data  <= wbu_isu_data_o;
    q_exu_wbu_wr_csr_en   <= q_exu_wbu_wr_csr_en_i;
    q_exu_wbu_wr_csr_addr <= q_exu_wbu_wr_csr_addr_i;
    debug_selClintR  <= ~ctrl_wbu_flush_i & debug_selClint;
    debug_selClintRR <= ~ctrl_wbu_flush_i & debug_selClintR;
  end
// skip read mycycle
wire csr_mcycle_skip = q_exu_wbu_wr_csr_en && q_exu_wbu_valid && (q_exu_wbu_wr_csr_addr == `CSR_ADDR_mcycle);

`ifdef PRINT_WBU
always@(posedge clk)
  if(wbu_ifu_redict_valid_o)begin
    $fwrite(32'h8000_0001, "{redict} [PC : 0x%08x] -> newPC %08x\n", q_exu_wbu_pc_i,wbu_ifu_redict_pc_o);//TODO 这里可以直连到regfile里
    $fflush();
  end
always@(posedge clk)
  if (q_exu_wbu_valid)begin
    $fwrite(32'h8000_0001, "{commit} [PC : 0x%08x] -> %08x\n", q_exu_wbu_pc,q_exu_wbu_inst);//TODO 这里可以直连到regfile里
    $fflush();
  end
`endif

always@(posedge clk)
  if((q_exu_wbu_inst_i == 32'h7b) && wbuValid)begin
    $fwrite(32'h8000_0001, "%c", debug_a0);//TODO 这里可以直连到regfile里
    $fflush();
  end


DifftestInstrCommit DifftestInstrCommit(
  .clock              (clk),
  .coreid             (0),
  .index              (0),
  .valid              (q_exu_wbu_valid),
  .pc                 (q_exu_wbu_pc   ),
  .instr              (q_exu_wbu_inst ),
  .special            (0),
  .skip               ((q_exu_wbu_inst == 32'h7b) || csr_mcycle_skip || debug_selClintRR),
  .isRVC              (0),
  .scFailed           (0),
  .wen                (q_wbu_isu_wen ),
  .wdest              (q_wbu_isu_addr),
  .wdata              (q_wbu_isu_data)
);
reg [63:0] cycleCnt;
reg [63:0] instrCnt;
always@(posedge clk)begin
  if(~rst_n)begin
      cycleCnt <= 'd0;
      instrCnt <= 'd0;
  end else begin
      cycleCnt <= cycleCnt + 1;
      instrCnt <= instrCnt + q_exu_wbu_valid;
  end
end

DifftestTrapEvent DifftestTrapEvent(
  .clock              (clk),
  .coreid             (0),
  .valid              ((q_exu_wbu_inst == 32'h6b) && q_exu_wbu_valid),
  .code               ('d0),
  .pc                 (q_exu_wbu_pc),
  .cycleCnt           (cycleCnt),
  .instrCnt           (instrCnt)
);
`endif

endmodule